﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ParallelRuntimeLibrary.Internal;

namespace ParallelRuntimeLibrary
{
    public struct SpinWait
    {
        private int mCount;
        

        /*///////////////////////////////////////////////////////////////////////////////////*/

        /// <summary>
        /// The number of times <see cref="ParallelRuntimeLibrary.SpinWait.SpinOnce" /> has been called on this instance.
        /// </summary>
        public int Count
        {
            get
            {
                return mCount;
            }
        }

        /// <summary>
        /// Whether the next call to <see cref="ParallelRuntimeLibrary.SpinWait.SpinOnce" /> will yield the processor.
        /// </summary>
        public bool NextSpinWillYield
        {
            get
            {
                if (NativeMethod.GetProcessorCount() > 1)
                    return (this.mCount >= 0x19);

                return true;
            }
        }

        /// <summary>
        /// Performs a single Spin iteration.
        /// </summary>
        /// <remarks>
        /// This is typically called in a loop, and may change is behavior based on the number of times a
        /// <see cref="ParallelRuntimeLibrary.SpinWait.SpinOnce" /> has been called thus far on this instance.
        /// </remarks>
        public void SpinOnce()
        {
            if (this.NextSpinWillYield)
            {
                int num = (this.mCount >= 0x19) ? (this.mCount - 0x19) : this.mCount;
                int tmp = num % 10;

                if (tmp == 7)
                    Thread.Sleep(1);
                else
                    NativeMethod.SwitchToThread();
            }
            else
                Thread.SpinWait(((int)(this.mCount * 1.28f)) + 1);
            

            this.mCount = (this.mCount == 0x7fffffff) ? 0x19 : (this.mCount + 1);
        }

        /// <summary>
        /// Spain waiting until something to happen
        /// </summary>
        /// <param name="condition">Spain waiting will wait until this condition become true</param>
        public void SpinUntil(Func<bool> condition)
        {
            while (!condition())
                SpinOnce();
        }

        /// <summary>
        /// Resets the internal spin counter.
        /// </summary>
        public void Reset()
        {
            this.mCount = 0;
        }
    }
}
